/**
 * Copyright (C) 2010 - 2013 Harry Glasgow
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.googlecode.jaden.ui.dialog.automatic;

import com.googlecode.jaden.common.config.IOConfiguration;
import com.googlecode.jaden.common.config.ProjectConfiguration;
import com.googlecode.jaden.common.enums.DateResultType;
import com.googlecode.jaden.common.enums.IOType;
import com.googlecode.jaden.common.other.JadenException;
import com.googlecode.jaden.common.other.SmartTokenizer;
import com.googlecode.jaden.common.other.Utils;
import com.googlecode.jaden.ui.Constants;
import com.googlecode.jaden.ui.UI;
import com.googlecode.jaden.ui.UiUtils;
import com.googlecode.jaden.ui.dialog.BaseDialog;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.prefs.Preferences;

import static com.googlecode.jaden.ui.Constants.*;

public class AutomaticProjectCreateDialog extends BaseDialog {

    private final JTextField nameField;

    private final JRadioButton spaceDelimitRB;
    private final JRadioButton tabDelimitRB;
    private final JRadioButton otherDelimitRB;
    private final JTextField delimitField;

    private final JCheckBox extractHeadingsCB;

    private final SpinnerNumberModel headingsLineSpinnerModel;
    private final JSpinner headingsLineSpinner;

    private final SpinnerNumberModel firstLineSpinnerModel;

    private final JCheckBox scanToEndCB;
    private final SpinnerNumberModel lastLineSpinnerModel;
    private final JSpinner lastLineSpinner;

    private final JTextField decimalSeparatorField;

    private final JRadioButton otherThousandsSeparatorRB;
    private final JTextField thousandsSeparatorField;

    private final JRadioButton otherStringLimiterRB;
    private final JTextField stringLimiterField;

    private final JTextField fileNameTF;

    private final JFileChooser chooser;

    private final JCheckBox applyNumericHeadroomCB;
    private final SpinnerNumberModel numericHeadroomSpinnerModel;
    private JSpinner numericHeadroomSpinner;

    private final JCheckBox coalesceDuplicateColumnSeparatorsCB;

    public AutomaticProjectCreateDialog(UI owner, Preferences preferences) {
        super(owner, DIALOG_AUTOMATIC_CREATE_TITLE);

        chooser = new JFileChooser();
        owner.loadDataDirectory(chooser);

        nameField = new JTextField(20);
        nameField.setText(Utils.translate(Constants.LABEL_UNTITLED_TEXT));
        nameField.addKeyListener(new KeyAdapter() {
            public void keyReleased(KeyEvent e) {
                enableOkButton();
            }
        });

        JLabel nameLabel = new JLabel(Utils.translate(LABEL_NAME_TEXT));

        spaceDelimitRB = new JRadioButton(Utils.translate(LABEL_DELIMITER_SPACE_TEXT));
        tabDelimitRB = new JRadioButton(Utils.translate(LABEL_DELIMITER_TAB_TEXT));
        otherDelimitRB = new JRadioButton(Utils.translate(LABEL_DELIMITER_OTHER_TEXT));

        delimitField = new JTextField(1);

        fileNameTF = new JTextField(20);
        fileNameTF.setEditable(false);
        JLabel fileNameLabel = new JLabel(Utils.translate(LABEL_DATA_FILE_TEXT));
        JButton fileButton = new JButton("...");

        ButtonGroup bg = new ButtonGroup();
        bg.add(spaceDelimitRB);
        bg.add(tabDelimitRB);
        bg.add(otherDelimitRB);
        JLabel columnSeparatorLabel = new JLabel(Utils.translate(LABEL_COLUMN_SEPARATOR_TEXT));

        extractHeadingsCB = new JCheckBox(Utils.translate(Constants.LABEL_EXTRACT_HEADINGS_TEXT));
        extractHeadingsCB.setSelected(true);

        coalesceDuplicateColumnSeparatorsCB = new JCheckBox(Utils.translate(LABEL_COALESCE_DUPLICATE_COLUMN_SEPARATORS_TEXT));

        headingsLineSpinnerModel = new SpinnerNumberModel(1, 1, 999, 1);
        headingsLineSpinner = new JSpinner(headingsLineSpinnerModel);
        JLabel headingsLineLabel = new JLabel(Utils.translate(LABEL_HEADINGS_LINE_TEXT));
        JLabel firstLineLabel = new JLabel(Utils.translate(LABEL_FIRST_LINE_TEXT));

        firstLineSpinnerModel = new SpinnerNumberModel(2, 1, 999, 1);
        JSpinner firstLineSpinner = new JSpinner(firstLineSpinnerModel);

        scanToEndCB = new JCheckBox(Utils.translate(LABEL_SCAN_TO_END_TEXT));
        scanToEndCB.setSelected(true);
        lastLineSpinnerModel = new SpinnerNumberModel(3, 1, 999, 1);
        lastLineSpinner = new JSpinner(lastLineSpinnerModel);
        JLabel lastLineLabel = new JLabel(Utils.translate(LABEL_LAST_LINE_TEXT));

        JLabel decimalSeparatorLabel = new JLabel(Utils.translate(LABEL_DECIMAL_SEPARATOR_TEXT));
        decimalSeparatorField = new JTextField(1);
        decimalSeparatorField.setText(".");

        JLabel thousandsSeparatorLabel = new JLabel(Utils.translate(LABEL_THOUSANDS_SEPARATOR_TEXT));
        JRadioButton noneThousandsSeparatorRB = new JRadioButton(Utils.translate(LABEL_NONE_TEXT));
        otherThousandsSeparatorRB = new JRadioButton(Utils.translate(LABEL_DELIMITER_OTHER_TEXT));
        thousandsSeparatorField = new JTextField(1);
        thousandsSeparatorField.setText(",");
        bg = new ButtonGroup();
        bg.add(noneThousandsSeparatorRB);
        bg.add(otherThousandsSeparatorRB);
        noneThousandsSeparatorRB.setSelected(true);

        JLabel stringLimiterLabel = new JLabel(Utils.translate(LABEL_STRING_LIMITER_TEXT));
        JRadioButton noneStringLimiterRB = new JRadioButton(Utils.translate(LABEL_NONE_TEXT));
        otherStringLimiterRB = new JRadioButton(Utils.translate(LABEL_DELIMITER_OTHER_TEXT));
        stringLimiterField = new JTextField(1);
        stringLimiterField.setText("\"");
        bg = new ButtonGroup();
        bg.add(noneStringLimiterRB);
        bg.add(otherStringLimiterRB);
        noneStringLimiterRB.setSelected(true);

        applyNumericHeadroomCB = new JCheckBox(Utils.translate(Constants.LABEL_APPLY_NUMERIC_HEADROOM_TEXT));
        applyNumericHeadroomCB.setSelected(true);

        numericHeadroomSpinnerModel = new SpinnerNumberModel(10, 1, 50, 1);
        numericHeadroomSpinner = new JSpinner(numericHeadroomSpinnerModel);
        JLabel numericHeadroomLabel = new JLabel(Utils.translate(Constants.LABEL_NUMERIC_HEADROOM_TEXT));
        JLabel numericHeadroomPercentLabel = new JLabel(Utils.translate(Constants.LABEL_PERCENT_TEXT));

        UiUtils.enWidth(thousandsSeparatorLabel, stringLimiterLabel);

        JPanel mainPanel = UiUtils.verticalList(UiUtils.horizontalList(nameLabel, nameField),
                UiUtils.horizontalList(fileNameLabel, fileNameTF, fileButton),
                UiUtils.horizontalList(columnSeparatorLabel, spaceDelimitRB, tabDelimitRB, otherDelimitRB, delimitField),
                UiUtils.horizontalList(coalesceDuplicateColumnSeparatorsCB),
                UiUtils.horizontalList(extractHeadingsCB),
                UiUtils.horizontalList(headingsLineLabel, headingsLineSpinner),
                UiUtils.horizontalList(firstLineLabel, firstLineSpinner),
                UiUtils.horizontalList(scanToEndCB),
                UiUtils.horizontalList(lastLineLabel, lastLineSpinner),
                UiUtils.horizontalList(decimalSeparatorLabel, decimalSeparatorField),
                UiUtils.horizontalList(thousandsSeparatorLabel, noneThousandsSeparatorRB, otherThousandsSeparatorRB, thousandsSeparatorField),
                UiUtils.horizontalList(stringLimiterLabel, noneStringLimiterRB, otherStringLimiterRB, stringLimiterField),
                UiUtils.horizontalList(applyNumericHeadroomCB),
                UiUtils.horizontalList(numericHeadroomLabel, numericHeadroomSpinner, numericHeadroomPercentLabel)
        );

        setMainPanel(mainPanel, preferences);

        spaceDelimitRB.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                enableDelimiter();
            }
        });
        tabDelimitRB.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                enableDelimiter();
            }
        });
        otherDelimitRB.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                enableDelimiter();
            }
        });
        otherDelimitRB.setSelected(true);
        delimitField.addKeyListener(new KeyAdapter() {
            public void keyReleased(KeyEvent e) {
                if (delimitField.getText().length() > 1) {
                    delimitField.setText(delimitField.getText().substring(0, 1));
                }
            }
        });
        delimitField.setText(",");
        decimalSeparatorField.addKeyListener(new KeyAdapter() {
            public void keyReleased(KeyEvent e) {
                if (decimalSeparatorField.getText().length() > 1) {
                    decimalSeparatorField.setText(decimalSeparatorField.getText().substring(0, 1));
                }
            }
        });

        noneThousandsSeparatorRB.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                enableThousandsSeparator();
            }
        });
        otherThousandsSeparatorRB.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                enableThousandsSeparator();
            }
        });
        noneStringLimiterRB.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                enableStringLimiter();
            }
        });
        otherStringLimiterRB.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                enableStringLimiter();
            }
        });

        extractHeadingsCB.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                enableHeadingsLine();
            }
        });

        scanToEndCB.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                enableLastLine();
            }
        });
        fileButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                selectFile();
            }
        });

        applyNumericHeadroomCB.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                enableNumericHeadroom();
            }
        });

        enableOkButton();
        enableHeadingsLine();
        enableLastLine();
        enableThousandsSeparator();
        enableStringLimiter();
        enableNumericHeadroom();
    }

    private void enableNumericHeadroom() {
        numericHeadroomSpinner.setEnabled(applyNumericHeadroomCB.isSelected());
    }

    private void selectFile() {
        int i = chooser.showOpenDialog(this);
        if (i == JFileChooser.APPROVE_OPTION) {
            ((UI) getOwner()).storeDataDirectory(chooser);
            fileNameTF.setText(chooser.getSelectedFile().getAbsolutePath());
            enableOkButton();
            enableDelimiter();
        }
    }

    private void enableLastLine() {
        lastLineSpinner.setEnabled(!scanToEndCB.isSelected());
    }

    /**
     * One ugly spud of a method. Oh well...
     *
     * @throws JadenException
     */
    protected void doOk() throws JadenException {
        boolean cool = true;
        String message = "";
        List<ColumnMetaData> columnMetaDataList = new ArrayList<ColumnMetaData>();
        String fileName = fileNameTF.getText();
        UiUtils.setWaitCursor(this.getRootPane());

        try {

            // What's the column delimiter?
            String columnDelimiter = " ";
            if (otherDelimitRB.isSelected()) {
                columnDelimiter = delimitField.getText();
            } else if (tabDelimitRB.isSelected()) {
                columnDelimiter = "\t";
            }

            // Load it.
            List<String> lines = new ArrayList<String>();
            if (fileName.endsWith(".xls")) {
                try {
                    FileInputStream fileInputStream = new FileInputStream(new File(fileName));
                    HSSFWorkbook workbook = new HSSFWorkbook(fileInputStream);
                    HSSFSheet worksheet = workbook.getSheetAt(0);
                    int firstRowNum = worksheet.getFirstRowNum();
                    int lastRowNum = worksheet.getLastRowNum();
                    for (int rowNum = firstRowNum; rowNum < lastRowNum; rowNum++) {
                        StringBuilder sb = new StringBuilder();
                        HSSFRow row = worksheet.getRow(rowNum);
                        int firstCellNum = row.getFirstCellNum();
                        int lastCellNum = row.getLastCellNum();
                        for (int cellNum = firstCellNum; cellNum < lastCellNum; cellNum++) {
                            HSSFCell cell = row.getCell((short) cellNum);
                            if (cell.getCellType() == HSSFCell.CELL_TYPE_STRING) {
                                String value = cell.getStringCellValue();
                                sb.append(value);
                                if (cellNum < lastCellNum - 1) {
                                    sb.append(columnDelimiter);
                                }
                            } else if (cell.getCellType() == HSSFCell.CELL_TYPE_NUMERIC) {
                                double value = cell.getNumericCellValue();
                                sb.append(value);
                                if (cellNum < lastCellNum - 1) {
                                    sb.append(columnDelimiter);
                                }
                            }
                        }
                        lines.add(sb.toString());
                    }
                } catch (IOException e) {
                    cool = false;
                    message = e.getMessage();
                }
            } else {
                BufferedReader br = null;
                try {
                    br = new BufferedReader(new FileReader(fileName));
                    String line;
                    while ((line = br.readLine()) != null) {
                        lines.add(line);
                    }
                } catch (Exception e) {
                    cool = false;
                    message = e.getMessage();
                } finally {
                    if (br != null) {
                        try {
                            br.close();
                        } catch (IOException e) {
                            // So don't care.
                        }
                    }
                }
            }

            int columns = -1;

            // Headings
            int lineNumber = 1;
            String stringDelimiter = otherStringLimiterRB.isSelected() ? stringLimiterField.getText() : "";
            for (String line : lines) {
                if (extractHeadingsCB.isSelected() && lineNumber == headingsLineSpinnerModel.getNumber().intValue()) {
                    SmartTokenizer st = new SmartTokenizer(line, columnDelimiter, stringDelimiter,
                            coalesceDuplicateColumnSeparatorsCB.isSelected());
                    if (st.getParts().length < 2) {
                        // Need at least one input and one output.
                        message = Utils.translate(ERROR_TOO_FEW_HEADINGS, String.valueOf(lineNumber));
                        cool = false;
                    } else {
                        int fileColumnNumber = 0;
                        for (String heading : st.getParts()) {
                            columnMetaDataList.add(new ColumnMetaData(fileColumnNumber++, heading));
                        }
                        columns = st.getParts().length;
                    }
                    break;
                }
                lineNumber++;
            }

            // Data
            lineNumber = 1;
            List<List<String>> dataList = new ArrayList<List<String>>();
            for (String line : lines) {
                if (lineNumber >= firstLineSpinnerModel.getNumber().intValue() &&
                        (scanToEndCB.isSelected() || lineNumber <= lastLineSpinnerModel.getNumber().intValue())) {
                    SmartTokenizer st = new SmartTokenizer(line, columnDelimiter, stringDelimiter,
                            coalesceDuplicateColumnSeparatorsCB.isSelected());
                    String[] dataStrings = st.getParts();
                    if (columns == -1) {
                        // No headings yet, so use this one.
                        if (dataStrings.length < 2) {
                            // Need at least one input and one output.
                            message = Utils.translate(ERROR_TOO_FEW_DATA, String.valueOf(lineNumber));
                            cool = false;
                            break;
                        } else {
                            columns = dataStrings.length;
                        }
                    } else {
                        if (dataStrings.length != columns) {
                            message = Utils.translate(ERROR_DIFFERENT_DATA, String.valueOf(lineNumber));
                            cool = false;
                            break;
                        }
                    }
                    List<String> dataSet = new ArrayList<String>();
                    Collections.addAll(dataSet, dataStrings);
                    dataList.add(dataSet);
                }
                lineNumber++;
            }

            if (cool) {
                if (dataList.isEmpty()) {
                    message = Utils.translate(Constants.ERROR_NO_DATA);
                    cool = false;
                } else if (columnMetaDataList.isEmpty()) {
                    // Knock up some default headings.
                    for (List<String> strings : dataList) {
                        for (int i = 0; i < strings.size(); i++) {
                            if (i == strings.size() - 1) {
                                columnMetaDataList.add(new ColumnMetaData(i, Utils.translate(Constants.LABEL_OUTPUT_TEXT)));
                            } else {
                                columnMetaDataList.add(new ColumnMetaData(i, Utils.translate(Constants.LABEL_INPUT_TEXT) + " " + (i + 1)));
                            }
                        }
                        // Just want to do this for the first line, so stop here.
                        break;
                    }
                }
            }

            // Data

            if (cool) {

                // Figure out what each column is.
                boolean firstLine = true;
                SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");
                outer:
                for (List<String> strings : dataList) {

                    int j = 0;
                    for (String string : strings) {
                        string = string.trim();

                        ColumnMetaData metaData = columnMetaDataList.get(j);

                        if (firstLine) {

                            // Default categorical.
                            ColumnMetaData.Type type = ColumnMetaData.Type.Categorical;

                            // Numeric?
                            try {
                                Double.parseDouble(string);
                                type = ColumnMetaData.Type.Numeric;
                            } catch (NumberFormatException e) {
                                // Don't care.
                            }

                            // Date?
                            try {
                                dateFormat.parse(string);
                                type = ColumnMetaData.Type.Date;
                            } catch (ParseException e) {
                                // Don't care
                            }
                            metaData.setType(type);
                        }

                        if (metaData.getType() == ColumnMetaData.Type.Categorical) {
                            // Add to the categories.
                            metaData.getCategories().add(string);
                        } else if (metaData.getType() == ColumnMetaData.Type.Numeric) {
                            // Numeric?
                            try {
                                // Adjust the min max limits.
                                double d = Double.parseDouble(string);
                                if (d > metaData.getMax()) {
                                    metaData.setMax((int) Math.round(d));
                                }
                                if (d < metaData.getMin()) {
                                    metaData.setMin((int) Math.round(d));
                                }
                            } catch (NumberFormatException e) {
                                // Not numeric on this line?
                                message = Utils.translate(ERROR_BAD_NUMERIC, string);
                                cool = false;
                                break outer;
                            }
                        } else if (metaData.getType() == ColumnMetaData.Type.Date) {
                            try {
                                dateFormat.parse(string);
                            } catch (ParseException e) {
                                // Not date on this line?
                                message = Utils.translate(ERROR_BAD_DATE, string);
                                cool = false;
                                break outer;
                            }
                        }

                        j++;
                    }

                    firstLine = false;
                }
            }

            // Apply headroom to numeric columns
            if (cool && applyNumericHeadroomCB.isSelected()) {
                for (ColumnMetaData columnMetaData : columnMetaDataList) {
                    if (columnMetaData.getType() == ColumnMetaData.Type.Numeric) {
                        int min = columnMetaData.getMin();
                        int max = columnMetaData.getMax();
                        int percentage = numericHeadroomSpinnerModel.getNumber().intValue();
                        int[] newRange = calculateHeadroom(min, max, percentage);
                        columnMetaData.setMin(newRange[0]);
                        columnMetaData.setMax(newRange[1]);
                    }
                }
            }
        } finally {
            UiUtils.clearCursor(this.getRootPane());
        }

        // See what columns the user actually wants.
        AtomicInteger outputColumnNumber = new AtomicInteger(columnMetaDataList.size() - 1);
        if (cool) {

            // If for some reason we have duplicate headings, skip, as this is not gonna work.
            List<String> dupes = new ArrayList<String>();
            boolean gotDupes = false;
            for (ColumnMetaData data : columnMetaDataList) {
                String heading = data.getHeading();
                if (dupes.contains(heading)) {
                    gotDupes = true;
                    break;
                } else {
                    dupes.add(heading);
                }
            }

            if (!gotDupes) {
                AutomaticColumnSelectorDialog dialog = new AutomaticColumnSelectorDialog(this, getPreferences(),
                        columnMetaDataList, outputColumnNumber);
                dialog.setVisible(true);
            }
        }

        ProjectConfiguration project = new ProjectConfiguration();
        if (cool) {
            project.setName(nameField.getText());
            if (otherDelimitRB.isSelected()) {
                project.setColumnSeparator(delimitField.getText());
            } else if (tabDelimitRB.isSelected()) {
                project.setColumnSeparator("\t");
            } else {
                project.setColumnSeparator(" ");
            }
            project.setDecimalSeparator(decimalSeparatorField.getText());
            project.setFirstLine(firstLineSpinnerModel.getNumber().intValue());
            project.setLastLine(lastLineSpinnerModel.getNumber().intValue());
            project.setScanToEnd(scanToEndCB.isSelected());
            project.setUseThousandsSeparator(otherThousandsSeparatorRB.isSelected());
            project.setThousandsSeparator(otherThousandsSeparatorRB.isSelected() ? thousandsSeparatorField.getText() : ",");
            project.setUseStringLimiter(otherStringLimiterRB.isSelected());
            project.setStringLimiter(otherStringLimiterRB.isSelected() ? stringLimiterField.getText() : "\"");
            project.setCoalesceDuplicateColumnSeparators(coalesceDuplicateColumnSeparatorsCB.isSelected());

            // IOConfigs
            IOConfiguration[] inputConfigurations = new IOConfiguration[columnMetaDataList.size() - 1];
            project.setInputConfigurations(inputConfigurations);
            int inputPointer = 0;
            for (int i = 0; i < columnMetaDataList.size(); i++) {
                ColumnMetaData metaData = columnMetaDataList.get(i);
                IOConfiguration ioConfiguration = new IOConfiguration(i == outputColumnNumber.get());
                ioConfiguration.setName(metaData.getHeading());
                if (i == outputColumnNumber.get()) {
                    project.setOutputConfiguration(ioConfiguration);
                } else {
                    inputConfigurations[inputPointer++] = ioConfiguration;
                }
                if (metaData.getType() == ColumnMetaData.Type.Categorical) {
                    ioConfiguration.setType(IOType.Categorical);
                    for (String s : metaData.getCategories()) {
                        ioConfiguration.getCategoricalList().add(s);
                    }
                } else if (metaData.getType() == ColumnMetaData.Type.Date) {
                    ioConfiguration.setType(IOType.Date);
                    ioConfiguration.setDateFormat("dd/MM/yyyy");
                    ioConfiguration.setDateResultType(DateResultType.DayOfMonth);
                } else {
                    ioConfiguration.setType(IOType.Numeric);
                    ioConfiguration.setNumericMinValue(metaData.getMin());
                    ioConfiguration.setNumericMaxValue(metaData.getMax());
                }

                // Training file
                project.setTrainingFilePath(fileName);
                project.setValidationFromTrainingFile(true);
                project.setValidationDataPercentage(10);
                project.setTestingFromTrainingFile(true);
                project.setTestingDataPercentage(10);
            }

            // IO column mappings
            project.setInputColumnMappings(new int[columnMetaDataList.size() - 1]);
            inputPointer = 0;
            for (int i = 0; i < columnMetaDataList.size(); i++) {
                ColumnMetaData data = columnMetaDataList.get(i);
                if (outputColumnNumber.get() == i) {
                    project.setOutputColumnMapping(data.getFileColumnNumber());
                } else {
                    project.getInputColumnMappings()[inputPointer++] = data.getFileColumnNumber();
                }
            }
        }

        if (cool) {
            ((UI) getOwner()).setProject(project);
            dispose();
        } else {
            JOptionPane.showMessageDialog(this, message);
        }
    }

    /**
     * Calculate an 'n' percent spread from the min and max.
     *
     * @param min
     * @param max
     * @param percentage
     * @return
     */
    static int[] calculateHeadroom(int min, int max, int percentage) {
        double midPoint = ((min + max) / 2.0);
        double difference = ((max - min) / 2.0);
        int[] minMax = new int[2];
        difference = (int) (difference * (100.0 + percentage) / 100.0);
        minMax[0] = (int)(midPoint - difference);
        minMax[1] = (int)(midPoint + difference);
        return minMax;
    }

    private void enableThousandsSeparator() {
        thousandsSeparatorField.setEditable(otherThousandsSeparatorRB.isSelected());
    }

    private void enableStringLimiter() {
        stringLimiterField.setEditable(otherStringLimiterRB.isSelected());
    }

    private void enableOkButton() {
        okButton.setEnabled(nameField.getText().trim().length() > 0 && !fileNameTF.getText().trim().isEmpty());
    }

    private void enableDelimiter() {
        if (Utils.isBlankOrNull(fileNameTF.getText()) || !fileNameTF.getText().endsWith(".xls")) {
            spaceDelimitRB.setEnabled(true);
            tabDelimitRB.setEnabled(true);
            otherDelimitRB.setEnabled(true);
            delimitField.setEnabled(otherDelimitRB.isSelected());
        } else {
            spaceDelimitRB.setEnabled(false);
            tabDelimitRB.setEnabled(false);
            otherDelimitRB.setEnabled(false);
            delimitField.setEnabled(false);
        }

    }

    private void enableHeadingsLine() {
        headingsLineSpinner.setEnabled(extractHeadingsCB.isSelected());
    }

}
